--  -------------------------------------------------------------------------
--  @File: comment_parser.ads
--
--  @Project: LiberAda
--  @Version: 1.0
--  @Created: 2009-10-19
--  @Author:  Markus Knau�
--
--  @Description:
--    Parser for the comment sections. This parser translates the comments
--    in an Ada source files to the document object model as described
--    within (@see: document.ads) and (@see: part.ads).
--
--  @Usage:
--    When a comment block begins call (@see: Start_Comment) then on any
--    line of the comment block call (@see: Add_Line). When the comment
--    block ends call (@see: Finish_Comment). After that the
--    Comment_Type's variable is ready initialized.
--    (@see: Get_Paragraph_Type) and (@see: Add_To_Document) can then be
--    safely called.
--
--  @Modifications:
--    +2009-11-17 MK: Initial release
with Ada.Strings.Unbounded;
with Ada.Containers.Vectors;

with Part;

package Comment_Parser is

   --  A comment formatted according to the Ada style guide is a set of
   --  paragraphs. Each paragraph is introduced with a @PARAGRAPH_IDENTIFIER:
   --  token. In a paragraph there can be multiple clauses or list entries.
   --  Clauses are divided by empty lines. List entries are introduced by
   --  +LIST_ITEM_LABEL: token.
   --
   --  Paragraph identifier tokens and list item labels must be the first
   --  words in a clause in a paragraph. Otherwise they are ignored.
   --
   --  Special entries in a comment's clause are cross references that are
   --  written @see SYMBOL or (@see SYMBOL_STRING). These can occur at any
   --  position in a comment's clauses but they must be on one line.
   --
   --  The structure of a comment is preserved with in the data type that
   --  returns the stored comment.

   package ASU renames Ada.Strings.Unbounded;

   --  ----------------------------------------------------------------------
   --  @Type: Comment_Type
   --  @Purpose: Holds the comment's structure
   type Comment_Type is tagged private;

   --  ----------------------------------------------------------------------
   --  @Type: Paragraph_Type
   --  @Purpose: The type of the paragraphs in a comment block.
   type Paragraph_Type is (
      MAIN_T,
      FILE_T,
      PROJECT_T,
      VERSION_T,
      CREATED_T,
      AUTHOR_T,
      ENVIRONMENT_T,
      DESCRIPTION_T,
      USAGE_T,
      MODIFICATIONS_T,
      RESTRICTIONS_T,
      ERROR_HANDLING_T,
      NOTES_T,
      IMPLEMENTATION_T,
      FUNCTION_T,
      PROCEDURE_T,
      PARAMETER_T,
      RETURN_T,
      PRE_T,
      POST_T,
      TYPE_T,
      CONSTANT_T,
      EXCEPTION_T,
      GLOBAL_VARIABLE_T,
      PURPOSE_T,
      REFERENCES_T,
      UNKNOWN_T
   );

   --  ----------------------------------------------------------------------
   --  @Function: Start_Comment
   --  @Description: Starts a comment by initializing the Comment_Type
   --    object.
   --  @Return: Initialized Comment_Type object
   function Start_Comment return Comment_Type;

   --  ----------------------------------------------------------------------
   --  @Procedure: Add_Line
   --  @Description:
   --    Adds a new line of the comment block to the Comment_Type object.
   --  @Parameter:
   --    +This: the comment to add the line to
   --    +Line: the line to add to the comment
   procedure Add_Line (This : in out Comment_Type; Line : in String);

   --  ----------------------------------------------------------------------
   --  @Procedure: Add_Line
   --  @Description:
   --    Adds a new line of the comment block to the Comment_Type object.
   --  @Parameter:
   --    +This: the comment to add the line to
   --    +Line: the line to add to the comment
   procedure Add_Line
     (This : in out Comment_Type;
      Line : in     ASU.Unbounded_String);

   --  ----------------------------------------------------------------------
   --  @Procedure: Finish_Comment
   --  @Description:
   --    Finishes this comment. After calling that procedure the comment
   --    can be translated to the document's object model by calling
   --    (@see: Add_To_Document).
   procedure Finish_Comment (This : in out Comment_Type);

   --  ----------------------------------------------------------------------
   --  @Function: Get_Paragraph_Type
   --  @Description: Returns the type of this paragraph
   --  @Return: Type of this paragraph (@see: Paragraph_Type)
   function Get_Paragraph_Type
     (This : in Comment_Type) return Paragraph_Type;

   --  ----------------------------------------------------------------------
   --  @Procedure: Add_To_Document
   --  @Description:
   --    Translates this comment to the document's object model and adds it
   --    as a sub-tree to the given document.
   --  @Parameter:
   --    +This: the comment to translate to the document's DOM
   --    +Doc: the document to add the comment's DOM as sub-tree.
   procedure Add_To_Document
     (This : in     Comment_Type;
      Doc  : in out Part.Part_Type);

   function Get_Title (This : in Comment_Type) return String;
private

   type Sentence_Types is (Simple, List);

   type Clause_Type (Sentence_Type : Sentence_Types := Simple) is record
      Sentence : ASU.Unbounded_String := ASU.Null_Unbounded_String;
      case Sentence_Type is
         when List =>
           Label : ASU.Unbounded_String := ASU.Null_Unbounded_String;
         when others =>
           null;
      end case;
   end record;

   function "=" (Left, Right : in Clause_Type) return Boolean;
   pragma Inline ("=");

   package Clause_Vectors is new Ada.Containers.Vectors
     (Index_Type   => Natural,
      Element_Type => Clause_Type,
      "="          => "=");

   type Paragraph_Content_Type is tagged record
      Par_Type : Paragraph_Type := UNKNOWN_T;
      Clauses  : Clause_Vectors.Vector := Clause_Vectors.Empty_Vector;
   end record;

   package Paragraph_Vectors is new Ada.Containers.Vectors
     (Index_Type   => Natural,
      Element_Type => Paragraph_Content_Type,
      "="          => "=");

   type Comment_Type is tagged record
      Paragraphs : Paragraph_Vectors.Vector := Paragraph_Vectors.Empty_Vector;
   end record;

   type Paragraph_Identifier_Mapping_Type is record
      Identifier : ASU.Unbounded_String := ASU.Null_Unbounded_String;
      Par_Type   : Paragraph_Type := UNKNOWN_T;
   end record;

   type Paragraph_Identifier_Mappings_Type is
     array (Natural range <>) of Paragraph_Identifier_Mapping_Type;

   Paragraph_Identifier_Mappings :
     constant Paragraph_Identifier_Mappings_Type :=
     ((Identifier => ASU.To_Unbounded_String ("Main"), Par_Type => MAIN_T),
      (Identifier => ASU.To_Unbounded_String ("File"), Par_Type => FILE_T),
      (Identifier => ASU.To_Unbounded_String ("Project"),
        Par_Type => PROJECT_T),
      (Identifier => ASU.To_Unbounded_String ("Version"),
        Par_Type => VERSION_T),
      (Identifier => ASU.To_Unbounded_String ("Created"),
        Par_Type => CREATED_T),
      (Identifier => ASU.To_Unbounded_String ("Author"), Par_Type => AUTHOR_T),
      (Identifier => ASU.To_Unbounded_String ("Environment"),
        Par_Type => ENVIRONMENT_T),
      (Identifier => ASU.To_Unbounded_String ("Description"),
        Par_Type => DESCRIPTION_T),
      (Identifier => ASU.To_Unbounded_String ("Usage"),
        Par_Type => USAGE_T),
      (Identifier => ASU.To_Unbounded_String ("Modifications"),
        Par_Type => MODIFICATIONS_T),
      (Identifier => ASU.To_Unbounded_String ("Restrictions"),
        Par_Type => RESTRICTIONS_T),
      (Identifier => ASU.To_Unbounded_String ("Error_Handling"),
        Par_Type => ERROR_HANDLING_T),
      (Identifier => ASU.To_Unbounded_String ("Notes"), Par_Type => NOTES_T),
      (Identifier => ASU.To_Unbounded_String ("Implementation"),
        Par_Type => IMPLEMENTATION_T),
      (Identifier => ASU.To_Unbounded_String ("Function"),
        Par_Type => FUNCTION_T),
      (Identifier => ASU.To_Unbounded_String ("Procedure"),
        Par_Type => PROCEDURE_T),
      (Identifier => ASU.To_Unbounded_String ("Parameter"),
        Par_Type => PARAMETER_T),
      (Identifier => ASU.To_Unbounded_String ("Return"),
        Par_Type => RETURN_T),
      (Identifier => ASU.To_Unbounded_String ("Pre"), Par_Type => PRE_T),
      (Identifier => ASU.To_Unbounded_String ("Post"), Par_Type => POST_T),
      (Identifier => ASU.To_Unbounded_String ("Type"), Par_Type => TYPE_T),
      (Identifier => ASU.To_Unbounded_String ("Constant"),
        Par_Type => CONSTANT_T),
      (Identifier => ASU.To_Unbounded_String ("Exception"),
        Par_Type => EXCEPTION_T),
      (Identifier => ASU.To_Unbounded_String ("Global_Variable"),
        Par_Type => GLOBAL_VARIABLE_T),
      (Identifier => ASU.To_Unbounded_String ("Purpose"),
        Par_Type => PURPOSE_T),
      (Identifier => ASU.To_Unbounded_String ("References"),
        Par_Type => REFERENCES_T));

   procedure Strip (Line : in out ASU.Unbounded_String);

   function Is_New_Paragraph (Line : in ASU.Unbounded_String) return Boolean;

   function Parse_Paragraph_Type
     (Line : in ASU.Unbounded_String) return Paragraph_Type;

   procedure Remove_Paragraph_Indicator (Line : in out ASU.Unbounded_String);

   function Is_List_Item_Start
     (Comment : in ASU.Unbounded_String) return Boolean;

   function Get_List_Label
     (Comment : in ASU.Unbounded_String) return ASU.Unbounded_String;

   function Strip_List_Label
     (Comment : in ASU.Unbounded_String) return ASU.Unbounded_String;

   procedure Print_Clause (Clause_Crsr : in Clause_Vectors.Cursor);

   procedure Print_Paragraph (Paragraph_Crsr : in Paragraph_Vectors.Cursor);

end Comment_Parser;
